package com.cn.wanxi.jwt.filter;

import com.cn.wanxi.jwt.util.JwtTokenUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @ClassName JwtAuthenticationTokenFilter
 * @Description 过滤拦截jwt
 * @Author JiJiang
 * @Date 2022/9/27 14:54
 * @Version 1.0
 */
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {


    private static final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Value("${jwt.tokenHeader}")//Authorization
    private String tokenHeader;

    @Value("${jwt.tokenHead}")
    private String tokenHead;//bearer

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws IOException, ServletException {
        //从 header  中获取 Authorization
        String authHeader = request.getHeader(this.tokenHeader);
        if (authHeader == null) {
            chain.doFilter(request, response);
            return;
        }
        // 判断 authHeader  不为空  并且以 bearer 开头
        boolean b1 = StringUtils.startsWithIgnoreCase(authHeader, this.tokenHead);
        if (!b1) {
            chain.doFilter(request, response);
            return;
        }
        //截取 bearer 后面的字符串  并且 两端去空格（获取token） -->获取jwt字符串
        String authToken = authHeader.substring(this.tokenHead.length()).trim();// The part after "Bearer "

        String username = jwtTokenUtil.getUserNameFromToken(authToken);


        System.out.println("====================JwtAuthenticationTokenFilter===========================");

        int hashCode = System.identityHashCode(SecurityContextHolder.getContext());

        System.out.println("========SecurityContextHolder.getContext()\t" + hashCode + "\t=====");
        System.out.println("========SecurityContextHolder.getContext().getAuthentication()\t" + SecurityContextHolder.getContext().getAuthentication() + "\t==");

        LOGGER.info("checking username:{}", username);
        // 用户名不为空  并且SecurityContextHolder.getContext()  存储 权限的容器中没有相关权限则继续
        boolean isNotAuthentication = SecurityContextHolder.getContext().getAuthentication() == null;
        if (username != null && isNotAuthentication) {
            //从数据库读取用户信息
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
            //校验token
            if (jwtTokenUtil.validateToken(authToken, userDetails)) {
                // 一个实现的带用户名和密码以及权限的Authentication(spring 自带的类)
                UsernamePasswordAuthenticationToken authentication = null;
                authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                // 从HttpServletRequest 对象，创建一个WebAuthenticationDetails对象
                WebAuthenticationDetails details = new WebAuthenticationDetailsSource().buildDetails(request);
                //设置details
                authentication.setDetails(details);
                LOGGER.info("authenticated user:{}", username);

                //存入本线程的安全容器   在访问接口拿到返回值后 要去主动清除 权限，避免干扰其他的线程
                //SecurityContextHolder原本会把authentication放入到session里，供后面使用
                //但是这是取消了session的使用，即将authentication对象存放在了线程中
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        chain.doFilter(request, response);
    }

}
